package com.tianyu.inspection.controller;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tianyu.inspection.common.ApiModel;
import com.tianyu.inspection.common.Constants;
import com.tianyu.inspection.common.GeoUtils;
import com.tianyu.inspection.common.annotation.Permission;
import com.tianyu.inspection.common.enums.FileTypeEnum;
import com.tianyu.inspection.common.enums.RoState;
import com.tianyu.inspection.entity.BasicInformation;
import com.tianyu.inspection.entity.PipelinePoint;
import com.tianyu.inspection.entity.model.QueryMo;
import com.tianyu.inspection.entity.vo.Ro;
import com.tianyu.inspection.entity.vo.ServiceRo;
import com.tianyu.inspection.service.api.IBasicInformationService;
import com.tianyu.inspection.service.api.IUploadFileService;
import io.ocs.common.OcsConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**********************************
 * @Author YSW
 * @Description
 * @Date 2023/9/12 - 16:30
 **********************************/

@Slf4j
@RestController
@RequestMapping("/api/2.0/basic-information")
public class BasicInformationController {

    @Resource
    private IBasicInformationService basicInformationService;

    @Resource
    private IUploadFileService uploadFileService;

    @Resource
    private OcsConstants ocsConstants;

    @GetMapping("/list/baseType/{id}")
    @Permission(value = 3, tags = Constants.API_COMMON)
    public Ro<?> getBasicInformationTypeList(HttpServletRequest request, @RequestHeader("u-projectId") Integer projectId, @PathVariable String id) {
        JSONObject queryMo = ApiModel.getJSONObject(request);
        queryMo.put("projectId", projectId);
        queryMo.put("baseTypeId", id);
        return new Ro<>().setContent(basicInformationService.searchListTypePage(queryMo));
    }

    @GetMapping("/list")
    @Permission(value = 3, tags = Constants.API_COMMON)
    public Ro<?> getBasicInformationList(HttpServletRequest request, @RequestHeader("u-projectId") Integer projectId) {
        JSONObject queryMo = ApiModel.getJSONObject(request);
        queryMo.put("projectId", projectId);
        return new Ro<>().setContent(basicInformationService.searchListPage(queryMo));
    }

    @GetMapping("/tree/baseType/{id}")
    @Permission(value = 3, tags = Constants.API_COMMON)
    public Ro<?> getBasicInformationTypeTree(HttpServletRequest request, @RequestHeader("u-projectId") Integer projectId, @PathVariable String id) {
        JSONObject queryMo = ApiModel.getJSONObject(request);
        queryMo.put("projectId", projectId);
        queryMo.put("baseTypeId", id);
        final ServiceRo allData = basicInformationService.getBasicInformationTypeTree(queryMo);
        if (allData.getResult()) {
            return new Ro<>().setContent(allData.getData());
        } else {
            return new Ro<>().setResultEnum(RoState.FAIL);
        }
    }

    @GetMapping("/tree/all")
    @Permission(value = 3, tags = Constants.API_COMMON)
    public Ro<?> getBasicInformationTree(HttpServletRequest request, @RequestHeader("u-projectId") Integer projectId) {
        JSONObject queryMo = ApiModel.getJSONObject(request);
        queryMo.put("projectId", projectId);
        final ServiceRo allData = basicInformationService.getBasicInformationTree(queryMo);
        if (allData.getResult()) {
            return new Ro<>().setContent(allData.getData());
        } else {
            return new Ro<>().setResultEnum(RoState.FAIL);
        }
    }

    @GetMapping("/tree/level/{id}")
    @Permission(value = 3, tags = Constants.API_COMMON)
    public Ro<?> getBasicInformationTreeLevel(@PathVariable String id, @RequestHeader("u-projectId") Integer projectId) {
        final JSONObject jsonObject = new JSONObject();
        jsonObject.put("id", (Objects.equals(id, "-1") || "".equals(id)) ? null : id);
        jsonObject.put("projectId", projectId);
        final ServiceRo allData = basicInformationService.getBasicInformationTreeLevel(jsonObject);
        if (allData.getResult()) {
            return new Ro<>().setContent(allData.getData());
        } else {
            return new Ro<>().setResultEnum(RoState.FAIL);
        }
    }


    @PostMapping(value = "/kml-json-load-prepare")
    @Permission(value = 3, tags = Constants.API_COMMON)
    public Ro<Object> kmlJsonLoadPrepare(@RequestBody JSONObject jsonObject) {
        Ro<Object> ro = new Ro<>();
        final Integer loadType = jsonObject.getInteger("mapLayerType");
        final JSONObject kmlJSONObject = jsonObject.getJSONObject("kmlData");
        return ro.setContent(basicInformationService.kmlJsonLoadPrepare(kmlJSONObject, loadType));
    }

    @PostMapping("/tree/load/kml-json")
    @Permission(value = 2, tags = Constants.API_ADMIN)
    public Ro<?> kmlJsonLoad(@RequestBody JSONObject jsonObject,
                             @RequestHeader("u-projectId") Integer projectId,
                             @RequestHeader("u-uid") Integer userId) {
        final Integer parentId = jsonObject.getInteger("parentId");
        final Integer typeDefId = jsonObject.getInteger("typeDefId");
        final Boolean loadFolder = jsonObject.getBoolean("loadFolder");
        final Integer mapLayerType = jsonObject.getInteger("mapLayerType");
        final JSONObject loadKmlJson = jsonObject.getJSONObject("kmlData");
        return new Ro<>().setContent(basicInformationService.loadXmlJson(loadKmlJson, parentId, typeDefId, loadFolder, mapLayerType, projectId, userId));
    }

    @PostMapping("/batch-insert-prepare")
    @Permission(value = 2, tags = Constants.API_ADMIN)
    public Ro<?> batchInsertPrepare(@RequestBody JSONObject jsonObject) {
        final JSONArray batchData = jsonObject.getJSONArray("batchData");
        final ArrayList<BasicInformation> bis = new ArrayList<>();
        for (Object o : batchData) {
            final BasicInformation basicInformation = JSON.to(BasicInformation.class, o);
            if (basicInformation.getJsonContent() == null) {
                continue;
            }
            bis.add(basicInformation);
        }
        return new Ro<>().setContent(JSONObject.of("count", bis.size()));
    }

    @PostMapping("/batch-insert")
    @Permission(value = 2, tags = Constants.API_ADMIN)
    public Ro<?> batchInsert(@RequestBody JSONObject jsonObject,
                             @RequestHeader("u-projectId") Integer projectId,
                             @RequestHeader("u-uid") Integer userId) {
        final Integer parentId = jsonObject.getInteger("parentId");
        final Integer typeDefId = jsonObject.getInteger("typeDefId");
        final Integer mapLayerType = jsonObject.getInteger("mapLayerType");
        final JSONArray batchData = jsonObject.getJSONArray("batchData");
        final ArrayList<BasicInformation> bis = new ArrayList<>();
        for (Object o : batchData) {
            final BasicInformation basicInformation = JSON.to(BasicInformation.class, o);
            if (basicInformation.getJsonContent() == null) {
                continue;
            }
            basicInformation.setFolder(false);
            basicInformation.setParentId(parentId);
            basicInformation.setMapLayerType(mapLayerType);
            basicInformation.setTypeDefId(typeDefId);
            basicInformation.setProjectId(projectId);
            bis.add(basicInformation);
        }
        if (bis.size() == 0) {
            return new Ro<>().setResultEnum(RoState.FAIL).setMessage("导入数据失败, 参数数据为空");
        }
        final ServiceRo serviceRo = basicInformationService.batchInsert(bis);
        if (serviceRo.getResult()) {
            return new Ro<>().setContent(serviceRo.getData());
        }
        return new Ro<>().setResultEnum(RoState.FAIL).setMessage(serviceRo.getMessage());
    }


    @PostMapping(value = "/insert")
    @Permission(value = 2, tags = Constants.API_ADMIN)
    public Ro<?> basicInsert(@RequestBody JSONObject jsonObject, @RequestHeader("u-projectId") Integer projectId) {
        Ro<Object> ro = new Ro<>(RoState.SUCCESS);
        final BasicInformation basicInformation = jsonObject.toJavaObject(BasicInformation.class);
        basicInformation.setProjectId(projectId);
        basicInformation.setId(null);
        if (basicInformation.getFolder() == null || !basicInformation.getFolder()) {
            if (basicInformation.getJsonContent() == null) {
                ro.setResultEnum(RoState.FAIL).setMessage("参数内容未设置");
                return ro;
            }
        }
        if (!basicInformationService.insert(basicInformation)) {
            ro.setResultEnum(RoState.FAIL);
        }
        return ro;
    }


    @PostMapping(value = "/line/electronicFence")
    @Permission(value = 4, tags = Constants.API_PATROL)
    public Ro<Object> electronicFence(@RequestBody QueryMo queryMo,
                                      @RequestHeader("u-projectId") Integer projectId) {
        Ro<Object> ro = new Ro<>(RoState.PARAMS_MISS_ERROR);
        Integer distance = (Integer) queryMo.get("distance");
        if (Objects.isNull(distance)) {
            return ro.setMessage("缺少参数[distance]");
        }
        if (distance == 0) {
            return ro.setMessage("电子围栏距离不能为0");
        }
        if (queryMo.get("points") != null) {
            try {
                JSONArray jsonArray = new ObjectMapper().convertValue(queryMo.get("points"), JSONArray.class);
                List<PipelinePoint> pointList = GeoUtils.queryBuffer(jsonArray, distance);
                return ro.setResultEnum(RoState.SUCCESS).setContent(pointList);
            } catch (Exception e) {
                return ro.setMessage("位置点解析失败");
            }
        }
        queryMo.build(BasicInformation.class, projectId);
        final Object inMap1 = queryMo.get("inMap");
        final JSONObject queryObj = JSONObject.of("pageSize", -1, "eqParams", JSONObject.of("projectId", projectId, "state", 0));
        if (inMap1 != null) {
            final ObjectMapper objectMapper = new ObjectMapper();
            Map<String, JSONArray> inMap = objectMapper.convertValue(queryMo.get("inMap"), new TypeReference<Map<String, JSONArray>>() {
            });
            queryMo.setInMap(inMap);
            queryObj.put("inParams", inMap);
        }
        IPage<BasicInformation> pipelineList = basicInformationService.searchListPage(queryObj);
        if (pipelineList.getRecords().size() == 0) {
            return ro.setState(RoState.FAIL.getValue()).setMessage("未查询到线路，不能生成电子围栏");
        } else {
            final JSONObject jsonObject = new JSONObject();
            List<BasicInformation> pipelines = pipelineList.getRecords();
            pipelines.forEach(pipeline -> {
                JSONObject jsonParams = pipeline.getJsonContent();
                final JSONArray points = jsonParams.getJSONArray("linePoints");
                JSONArray jsonArray = new ObjectMapper().convertValue(points, JSONArray.class);
                jsonObject.put(String.valueOf(pipeline.getId()), GeoUtils.queryBuffer(jsonArray, distance));
            });
            ro.setContent(jsonObject);
            return ro.setResultEnum(RoState.SUCCESS);
        }
    }

    @PostMapping(value = "/gis-model/insert")
    @Permission(value = 2, tags = Constants.API_ADMIN)
    public Ro<?> insertGisModel(@RequestHeader("u-projectId") Integer projectId,
                                @RequestBody JSONObject jsonObject) {
        Ro<Object> ro = new Ro<>(RoState.SUCCESS);
        final Integer typeDefId = jsonObject.getInteger("typeDefId");
        final Integer objType = jsonObject.getInteger("objType");
        final Integer objCount = jsonObject.getInteger("objCount");
        final String name = jsonObject.getString("name");
        final String name0 = name == null ? "未命名模型" : name;
        final Integer parentId = jsonObject.getInteger("parentId");
        if (objType == null) {
            return ro.setResultEnum(RoState.FAIL).setMessage("类型[objType]参数错误");
        }
        if (objCount == null) {
            return ro.setResultEnum(RoState.FAIL).setMessage("对象数量[objCount]参数错误");
        }
        final FileTypeEnum fileTypeEnum = FileTypeEnum.find(objType);
        String pathPrefix = "/" + projectId + "/model" + "/";
        final BasicInformation.BasicInformationBuilder builder = BasicInformation.builder();
        final String modelId = UUID.randomUUID().toString().substring(0, 8);
        final JSONObject jsonParams = JSONObject.of("name", name0, "modelId", modelId);
        final JSONArray objPathForUpload = jsonObject.getJSONArray("objPathForUpload");
        String tileUrl;
        switch (fileTypeEnum) {
            case FILE_TWO_DIMENSIONAL_MODEL: {
                pathPrefix += "two/" + modelId + "/";
                builder.mapLayerType(5);
                tileUrl = ocsConstants.getResourcePrefix() + pathPrefix + "{z}/{x}/{y}.jpg";
                jsonParams.put("tileUrl", tileUrl);
            }
            break;
            case FILE_THREE_DIMENSIONAL_MODEL: {
                pathPrefix += "three/" + modelId + "/";
                builder.mapLayerType(6);
                String indexObjPath = null;
                for (Object o : objPathForUpload) {
                    final String objPath = String.valueOf(o);
                    if (objPath != null && objPath.contains("tileset.json")) {
                        if (indexObjPath == null || indexObjPath.length() > objPath.length()) {
                            indexObjPath = objPath;
                        }
                    }
                }
                if (indexObjPath == null) {
                    return ro.setResultEnum(RoState.FAIL).setMessage("模型文件格式异常");
                }
                tileUrl = ocsConstants.getResourcePrefix() + pathPrefix + indexObjPath;
                jsonParams.put("tileUrl", tileUrl);
            }
            break;
            case UNKNOWN:
            default: {
                return ro.setResultEnum(RoState.FAIL).setMessage("类型[objType]参数错误");
            }
        }
        if (objPathForUpload.size() != objCount) {
            return ro.setResultEnum(RoState.FAIL).setMessage("对象数量[objCount]参数错误, 与实际请求对象地址个数不相同");
        }
        jsonParams.put("objCount", objCount);
        jsonParams.put("objUploadedCount", 0);
        final BasicInformation basicInformation = builder
                .name(name0)
                .typeDefId(typeDefId)
                .projectId(projectId)
                .folder(false)
                .jsonContent(jsonParams)
                .parentId(parentId)
                .build();
        basicInformationService.insert(basicInformation);
        ro.setContent(JSONObject.of("modelId", modelId,
                "tileUrl", tileUrl,
                "objUrlForUpload", uploadFileService.ocsPutUrlGet(basicInformation.getId(), modelId, pathPrefix, objPathForUpload)));
        return ro;
    }


    @PostMapping(value = "/gis-model/recover")
    @Permission(value = 2, tags = Constants.API_ADMIN)
    public Ro<?> continueInsertGisModel(@RequestBody JSONObject jsonObject) {
        Ro<Object> ro = new Ro<>(RoState.SUCCESS);
        final String modelId = jsonObject.getString("modelId");
        if (modelId == null) {
            return ro.setResultEnum(RoState.FAIL).setMessage("对象数量[modelId]参数错误, 与实际请求对象地址个数不相同");
        }
        ro.setContent(JSONObject.of("modelId", modelId, "objUrlForUpload", uploadFileService.ocsPutUrlCacheGet(modelId)));
        return ro;
    }

    @PatchMapping(value = "/modify")
    @Permission(value = 2, tags = Constants.API_ADMIN)
    public Ro<BasicInformation> modify(@RequestBody JSONObject operateMo, @RequestHeader("u-projectId") Integer projectId) {
        Ro<BasicInformation> ro = new Ro<>();
        final BasicInformation basicInformation = operateMo.toJavaObject(BasicInformation.class);
        basicInformation.setProjectId(null);
        if (Objects.isNull(basicInformation.getId())) {
            return ro.setResultEnum(RoState.FAIL).setMessage("缺少[id]字段,请传参后重试");
        }
        if (!basicInformationService.updateById(basicInformation)) {
            ro.setResultEnum(RoState.FAIL).setMessage("参数无匹配,修改失败");
        }
        return ro;
    }

    @DeleteMapping(value = "/batch-delete")
    @Permission(value = 2, tags = Constants.API_ADMIN)
    public Ro<?> batchDelete(@RequestBody List<Integer> ids,@RequestHeader("u-projectId") Integer projectId) {
        Ro<Object> ro = new Ro<>();
        ServiceRo resultMap = basicInformationService.batchDelete(ids,projectId);
        if (!resultMap.getResult()) {
            return ro.setResultEnum(RoState.FAIL).setMessage(resultMap.getMessage());
        }
        return ro.setContent(resultMap.getData());
    }

    @DeleteMapping(value = "/delete/{id}")
    @Permission(value = 2, tags = Constants.API_ADMIN)
    public Ro<BasicInformation> delete(@PathVariable Integer id, @RequestHeader("u-projectId") Integer projectId) {
        Ro<BasicInformation> ro = new Ro<>();
        ServiceRo resultMap = basicInformationService.deleteById(id, projectId);
        if (!resultMap.getResult()) {
            return ro.setResultEnum(RoState.FAIL).setMessage(resultMap.getMessage());
        }
        return ro;
    }

}
